#pragma once

namespace gecko
{

	//---------------------------------------------------------------------------------------------

	template<typename T>
	StrongPtr<T>::StrongPtr()
		: m_refCount(0)
	{
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	StrongPtr<T>::StrongPtr(T * ptr)
	{
		m_refCount = new internal::RefCount<T>(ptr);
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	template<typename Ty> StrongPtr<T>::StrongPtr(const StrongPtr<Ty> & rhs)
		: m_refCount((internal::RefCount<T>*)rhs.m_refCount)
	{
		m_refCount->AddRef();
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	void StrongPtr<T>::operator =(const StrongPtr & rhs)
	{
		m_refCount = rhs.m_refCount;
		m_refCount->AddRef();
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	template<typename Ty> void StrongPtr<T>::operator =(const StrongPtr<Ty> & rhs)
	{
		m_refCount = (internal::RefCount<T>*)rhs.m_refCount;
		m_refCount->AddRef();
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	StrongPtr<T>::~StrongPtr()
	{
		if (m_refCount)
		{
			m_refCount->DecRef();
		}
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	StrongPtr<T>::StrongPtr(const StrongPtr & rhs)
		: m_refCount(rhs.m_refCount)
	{
		m_refCount->AddRef();
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	StrongPtr<T>::StrongPtr(const WeakPtr<T> & rhs)
		: m_refCount(rhs.m_refCount)
	{
		m_refCount->AddRef();
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	StrongPtr<T>::operator bool()
	{
		return m_refCount && m_refCount->m_strongCount > 0;
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	T * StrongPtr<T>::operator -> ()
	{
		return m_refCount->m_ptr;
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	const T * StrongPtr<T>::operator -> () const
	{
		return m_refCount->m_ptr;
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	T & StrongPtr<T>::operator * ()
	{
		return *m_refCount->m_ptr;
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	const T & StrongPtr<T>::operator * () const
	{
		return *m_refCount->m_ptr;
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	WeakPtr<T>::WeakPtr()
		: m_refCount(NULL)
	{
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	WeakPtr<T>::WeakPtr(const StrongPtr<T> & ptr)
		: m_refCount(ptr.m_refCount)
	{
		m_refCount->AddWeakRef();
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	WeakPtr<T>::~WeakPtr()
	{
		m_refCount->DecWeakRef();
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	void WeakPtr<T>::operator = (const StrongPtr<T> & ptr)
	{
		m_refCount = ptr.m_refCount;
		m_refCount->AddWeakRef();
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	bool WeakPtr<T>::IsValid()
	{
		return (m_refCount->m_strongCount > 0);
	}

	//---------------------------------------------------------------------------------------------

	template<typename T>
	StrongPtr<T> WeakPtr<T>::Lock()
	{
		return StrongPtr<T>(*this);
	}

	//---------------------------------------------------------------------------------------------

	namespace internal
	{

		// reference counting for smart pointers
		template<typename T>
		class RefCount
		{
		public:
			// construct from raw pointer
			RefCount(T * ptr);

			// increase strong reference count
			void AddRef();

			// decrease strong reference count
			void DecRef();

			// increase weak reference count
			void AddWeakRef();

			// decrease weak reference count
			void DecWeakRef();

			T* m_ptr;
			int m_strongCount;
			int m_weakCount;
		};

		//---------------------------------------------------------------------------------------------

		template<typename T>
		RefCount<T>::RefCount(T * ptr)
			: m_ptr(ptr)
			, m_strongCount(1)
			, m_weakCount(0)
		{
		}

		//---------------------------------------------------------------------------------------------

		template<typename T>
		void RefCount<T>::AddRef()
		{
			Atomic::Increment(&m_strongCount);
		}

		//---------------------------------------------------------------------------------------------

		template<typename T>
		void RefCount<T>::DecRef()
		{
			if (Atomic::Decrement(&m_strongCount) <= 0)
			{
				delete m_ptr;
				DecWeakRef();
			}
		}

		//---------------------------------------------------------------------------------------------

		template<typename T>
		void RefCount<T>::AddWeakRef()
		{
			Atomic::Increment(&m_weakCount);
		}

		//---------------------------------------------------------------------------------------------

		template<typename T>
		void RefCount<T>::DecWeakRef()
		{
			if (Atomic::Decrement(&m_weakCount) <= 0)
			{
				if (m_strongCount <= 0)
				{
					delete this;
				}
			}
		}

		//---------------------------------------------------------------------------------------------

	} // namespace internal

} // namespace gecko